package com.liang.practice.server.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.liang.practice.api.enums.AnswerStatusEnum;
import com.liang.practice.api.enums.CompleteStatusEnum;
import com.liang.practice.api.req.GetReportReq;
import com.liang.practice.api.req.GetScoreDetailReq;
import com.liang.practice.api.req.SubmitPracticeDetailReq;
import com.liang.practice.api.vo.RankVO;
import com.liang.practice.api.vo.ReportSkillVO;
import com.liang.practice.api.vo.ReportVO;
import com.liang.practice.api.vo.ScoreDetailVO;
import com.liang.practice.server.entity.po.*;
import com.liang.practice.server.mapper.*;
import com.liang.practice.server.rpc.UserInfo;
import com.liang.practice.server.rpc.UserRpc;
import com.liang.practice.server.service.PracticeDetailService;
import com.liang.practice.server.utils.DateUtils;
import com.liang.practice.server.utils.LoginUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class PracticeDetailServiceImpl implements PracticeDetailService {

    private final PracticeDetailMapper practiceDetailMapper;
    private final PracticeSetDetailMapper practiceSetDetailMapper;
    private final PracticeMapper practiceMapper;
    private final PracticeSetMapper practiceSetMapper;
    private final SubjectLabelMapper subjectLabelMapper;
    private final SubjectMappingMapper subjectMappingMapper;
    private final UserRpc userRpc;

    @Override
    public Boolean submit(SubmitPracticeDetailReq req) {
        PracticePO practicePO = new PracticePO();
        Long practiceId = req.getPracticeId();
        Long setId = req.getSetId();
        practicePO.setSetId(setId);
        String timeUse = req.getTimeUse();
        String hour = timeUse.substring(0, 2);
        String minute = timeUse.substring(2, 4);
        String second = timeUse.substring(4, 6);
        practicePO.setTimeUse(hour + ":" + minute + ":" + second);
        practicePO.setSubmitTime(DateUtils.parseStrToDate(req.getSubmitTime()));
        practicePO.setCompleteStatus(CompleteStatusEnum.COMPLETE.getCode());
        practicePO.setIsDeleted(0);
        practicePO.setCreatedBy(LoginUtil.getLoginId());
        practicePO.setCreatedTime(new Date());
        //计算正确率
        Integer correctCount = practiceDetailMapper.selectCorrectCount(practiceId);
        List<PracticeSetDetailPO> practiceSetDetailPOS = practiceSetDetailMapper.selectBySetId(setId);
        Integer totalCount = practiceSetDetailPOS.size();
        BigDecimal correctRate = new BigDecimal(correctCount).divide(new BigDecimal(totalCount), 4, BigDecimal.ROUND_HALF_UP)
                .multiply(new BigDecimal("100.00"));
        practicePO.setCorrectRate(correctRate);
        PracticePO po = practiceMapper.selectById(practiceId);
        if (Objects.isNull(po)) {
            practiceMapper.insert(practicePO);
        } else {
            practicePO.setId(practiceId);
            practiceMapper.update(practicePO);
        }
        practiceSetMapper.updateHeat(setId);
        //补充剩余题目的记录
        List<PracticeDetailPO> practiceDetailPOList = practiceDetailMapper.selectByPracticeId(practiceId);
        List<PracticeSetDetailPO> minusList = practiceSetDetailPOS.stream()
                .filter(item -> !practiceDetailPOList.stream()
                        .map(e -> e.getSubjectId())
                        .collect(Collectors.toList())
                        .contains(item.getSubjectId()))
                .collect(Collectors.toList());
        if (log.isInfoEnabled()) {
            log.info("题目差集{}", JSON.toJSONString(minusList));
        }
        if (CollUtil.isNotEmpty(minusList)) {
            minusList.forEach(e -> {
                PracticeDetailPO practiceDetailPO = new PracticeDetailPO();
                practiceDetailPO.setPracticeId(practiceId);
                practiceDetailPO.setSubjectType(e.getSubjectType());
                practiceDetailPO.setSubjectId(e.getSubjectId());
                practiceDetailPO.setAnswerStatus(0);
                practiceDetailPO.setAnswerContent("");
                practiceDetailPO.setIsDeleted(0);
                practiceDetailPO.setCreatedTime(new Date());
                practiceDetailPO.setCreatedBy(LoginUtil.getLoginId());
                practiceDetailMapper.insertSingle(practiceDetailPO);
            });
        }
        return true;
    }

    @Override
    public List<ScoreDetailVO> getScoreDetail(GetScoreDetailReq req) {
        Long practiceId = req.getPracticeId();
        List<ScoreDetailVO> list = new LinkedList<>();
        List<PracticeDetailPO> practiceDetailPOList = practiceDetailMapper.selectByPracticeId(practiceId);
        if (CollUtil.isEmpty(practiceDetailPOList)) {
            return Collections.emptyList();
        }
        practiceDetailPOList.forEach(po -> {
            ScoreDetailVO scoreDetailVO = new ScoreDetailVO();
            scoreDetailVO.setSubjectId(po.getSubjectId());
            scoreDetailVO.setSubjectType(po.getSubjectType());
            scoreDetailVO.setIsCorrect(po.getAnswerStatus());
            list.add(scoreDetailVO);
        });
        return list;
    }

    @Override
    public ReportVO getReport(GetReportReq req) {
        ReportVO reportVO = new ReportVO();
        Long practiceId = req.getPracticeId();
        PracticePO practicePO = practiceMapper.selectById(practiceId);
        Long setId = practicePO.getSetId();
        PracticeSetPO practiceSetPO = practiceSetMapper.selectById(setId);
        reportVO.setTitle(practiceSetPO.getSetName());
        List<PracticeDetailPO> practiceDetailPOList = practiceDetailMapper.selectByPracticeId(practiceId);
        if (CollUtil.isEmpty(practiceDetailPOList)) {
            return null;
        }
        int totalCount = practiceDetailPOList.size();
        List<PracticeDetailPO> correctPoList = practiceDetailPOList.stream().filter(e ->
                Objects.equals(e.getAnswerStatus(), AnswerStatusEnum.CORRECT.getCode())).collect(Collectors.toList());
        reportVO.setCorrectSubject(correctPoList.size() + "/" + totalCount);
        List<ReportSkillVO> reportSkillVOS = new LinkedList<>();
        Map<Long, Integer> totalMap = getSubjectLabelMap(practiceDetailPOList);
        Map<Long, Integer> correctMap = getSubjectLabelMap(correctPoList);
        totalMap.forEach((key, val) -> {
            ReportSkillVO skillVO = new ReportSkillVO();
            SubjectLabelPO labelPO = subjectLabelMapper.queryById(key);
            String labelName = labelPO.getLabelName();
            Integer correctCount = correctMap.get(key);
            if (Objects.isNull(correctCount)) {
                correctCount = 0;
            }
            skillVO.setName(labelName);
            BigDecimal rate = BigDecimal.ZERO;
            if (!Objects.equals(val, 0)) {
                rate = new BigDecimal(correctCount.toString()).divide(new BigDecimal(val.toString()), 4,
                        BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
            }
            skillVO.setStar(rate);
            reportSkillVOS.add(skillVO);
        });
        if (log.isInfoEnabled()) {
            log.info("获取到的正确率{}", JSON.toJSONString(reportSkillVOS));
        }
        reportVO.setSkill(reportSkillVOS);
        return reportVO;
    }

    private Map<Long, Integer> getSubjectLabelMap(List<PracticeDetailPO> practiceDetailPOList) {
        if (CollUtil.isEmpty(practiceDetailPOList)) {
            return Collections.emptyMap();
        }
        Map<Long, Integer> map = new HashMap<>();
        practiceDetailPOList.forEach(detail -> {
            Long subjectId = detail.getSubjectId();
            List<SubjectMappingPO> labelIdPO = subjectMappingMapper.getLabelIdsBySubjectId(subjectId);
            labelIdPO.forEach(po -> {
                Long labelId = po.getLabelId();
                if (Objects.isNull(map.get(labelId))) {
                    map.put(labelId, 1);
                    return;
                }
                map.put(labelId, map.get(labelId) + 1);
            });
        });
        if (log.isInfoEnabled()) {
            log.info("获取到的题目对应的标签map{}", JSON.toJSONString(map));
        }
        return map;
    }

    @Override
    public List<RankVO> getPracticeRankList() {
        List<RankVO> list = new LinkedList<>();
        List<PracticeRankPO> poList = practiceDetailMapper.getPracticeCount();
        if (CollUtil.isEmpty(poList)) {
            return list;
        }
        poList.forEach(e -> {
            RankVO rankVO = new RankVO();
            rankVO.setCount(e.getCount());
            UserInfo userInfo = userRpc.getUserInfo(e.getCreatedBy());
            rankVO.setName(userInfo.getNickname());
            rankVO.setAvatar(userInfo.getAvatar());
            list.add(rankVO);
        });
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean giveUp(Long practiceId) {
        practiceDetailMapper.deleteByPracticeId(practiceId);
        practiceMapper.deleteById(practiceId);
        return true;
    }
}
